package jesss.commands;

import com.amazonaws.AmazonClientException;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import jesss.options.ExtendedGnuParser;
import jesss.options.JesssOptions;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.MissingArgumentException;
import org.apache.commons.cli.Options;

import java.util.ArrayList;
import java.util.List;

/**
 * User: Lee
 * Date: 9/21/13
 * Time: 1:20 PM
 */
public class SearchForStringInObjectsCommand extends AbstractJesssCommand {

    /*

        Call list objects
        For each object summary, call SearchForStringInObject

     */
    private JesssOptions jesssOptions = null;
    private Options options = new Options();
    private String bucketName;
    private String keyPrefix;
    private String searchString;

    public SearchForStringInObjectsCommand(JesssOptions jesssOptions) throws Exception {
        this.jesssOptions = jesssOptions;
        setOptions();
        try {
            parseArgs(jesssOptions.getArgs());
        } catch (Exception e) {
            printUsage();
            throw e;
        }
    }

    @Override
    public CommandResult executeCommand() {

        CommandResult result = new CommandResult();

        try {

            List<S3ObjectSummary> objectSummaries = getObjectSummaries();

            for (S3ObjectSummary objectSummary : objectSummaries) {
                searchObjectForString(objectSummary);
            }

            result.setResultStatus(ResultStatus.Success);
        } catch (Exception e) {
            result.setResultStatus(ResultStatus.Error);
            result.setStatusMessage(String.format("Unable to search for files at \"%s/%s\".", bucketName, keyPrefix));
            addExceptionInfoToCommandResult(result, e);
        }

        return result;
    }

    private void searchObjectForString(S3ObjectSummary objectSummary) throws Exception {
        String key = objectSummary.getKey();

        JesssCommand searchObject = JesssCommandFactoryImpl.getInstance().createCommand(jesssOptions.getAmazonS3(),
                "so",
                jesssOptions.getShouldDisplayVerbose(),
                new String[] {"-bn", bucketName, "-k", key, "-s", searchString, "-ie"});

        CommandResult searchResult = searchObject.executeCommand();

        if (searchResult.getResultStatus().equals(ResultStatus.Error)) {
            throw new Exception(searchResult.getStatusMessage());
        }
    }

    private ListObjectsRequest getListObjectsRequest() {

        ListObjectsRequest listObjectRequest = new ListObjectsRequest().withBucketName(bucketName).withMaxKeys(1000);


        if (keyPrefix != null && !keyPrefix.isEmpty()) {
            listObjectRequest.withPrefix(keyPrefix);
        }

        return listObjectRequest;
    }

    private List<S3ObjectSummary> getObjectSummaries() throws Exception {
        List<S3ObjectSummary> s3ObjectSummaries = new ArrayList<>();

        try {
            ListObjectsRequest listObjectsRequest = getListObjectsRequest();
            ObjectListing objectListing;

            do {
                objectListing = jesssOptions.getAmazonS3().listObjects(listObjectsRequest);
                if (objectListing != null)  {
                    s3ObjectSummaries.addAll(objectListing.getObjectSummaries());
                    String nextMarker = objectListing.getNextMarker();
                    if (nextMarker != null && !nextMarker.isEmpty()) {
                        listObjectsRequest.setMarker(nextMarker);
                    }
                }
            } while(objectListing != null && listObjectsRequest.getMaxKeys() == objectListing.getObjectSummaries().size());

        } catch (AmazonClientException e) {
            throw e;
        }

        return s3ObjectSummaries;
    }

    private void setOptions() {
        options = new Options();
        options.addOption("bn", "bucketName", true, "Bucket name to list objects from");
        options.addOption("kp", "keyPrefix", true, "Key prefix");
        options.addOption("s", "searchString", true, "Search string (regular expression)");
    }

    private void parseArgs(String[] args) throws Exception {
        if (args == null || args.length == 0) {
            printUsage();
        }

        try {
            CommandLineParser parser = new ExtendedGnuParser(true);
            CommandLine cmd = parser.parse(options, args);

            if (cmd.hasOption("bn")) {
                bucketName = cmd.getOptionValue("bn");
            } else {
                throw new MissingArgumentException("Missing bucket name parameter.");
            }

            if (cmd.hasOption("kp")) {
                keyPrefix = cmd.getOptionValue("kp");
            } else {
                throw new MissingArgumentException("Missing key prefix parameter.");
            }

            if (cmd.hasOption("s")) {
                searchString = cmd.getOptionValue("s");
            } else {
                throw new MissingArgumentException("Missing search string parameter.");
            }

        } catch (Exception e) {
            printUsage();
            throw e;
        }
    }

    private void printUsage() {
        System.out.println("Usage:");
        System.out.println("jesss.jesss -cn searchObjects -bn <mybucket> -kp <key prefix> -s <regular expression search string>");
    }
}
